package com.jinmdz.fmis.common.wrapper;

import com.jinmdz.fmis.common.annotation.AnColumn;
import com.jinmdz.fmis.common.annotation.AnTable;
import com.jinmdz.fmis.common.model.PoiCell;
import com.jinmdz.fmis.common.model.PoiRow;
import com.jinmdz.fmis.common.model.PoiTable;
import com.jinmdz.fmis.common.model.PoiTitle;
import com.jinmdz.fmis.common.util.ReflectUtil;
import com.jinmdz.fmis.common.util.StringUtil;
import com.jinmdz.fmis.common.util.third.PoiUtil;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

/**
 * poi 封装处理类
 *
 * @author LiCongLu
 * @date 2020-01-03 08:54
 */
public final class PoiWrapper {

    /**
     * 解析生成Poi数据表
     *
     * @param clazz    数据实体类型
     * @param dataList 数据集合
     * @return
     * @author LiCongLu
     * @date 2020-01-03 08:56
     */
    public static <T> PoiTable createPoiTable(Class<T> clazz, ArrayList<T> dataList) {
        // 判断是否有标记
        if (!clazz.isAnnotationPresent(AnTable.class)) {
            return null;
        }

        // 当无数据时返回空
        if (dataList == null) {
            return null;
        }

        AnTable anTable = clazz.getAnnotation(AnTable.class);
        String sheetName = anTable.value();
        if (sheetName == null || sheetName.length() == 0) {
            sheetName = "Sheet1";
        }

        ArrayList<AnColumn> columns = new ArrayList<>();

        HashMap<String, PoiColumnData> columnMap = new HashMap<>(16);
        HashMap<String, Field> fieldMap = ReflectUtil.getFieldMapByClass(clazz);

        // 解析反射获取列数据
        PoiRow columnRow = new PoiRow();
        ArrayList<PoiCell> columnCells = new ArrayList<>();
        for (Map.Entry<String, Field> entry : fieldMap.entrySet()) {
            Field field = entry.getValue();
            if (field.isAnnotationPresent(AnColumn.class)) {
                String key = entry.getKey();
                AnColumn anColumn = field.getAnnotation(AnColumn.class);
                PoiCell cell = new PoiCell();
                cell.setColumnIndex(anColumn.index())
                        .setCellValue(anColumn.value());
                if (cell.getCellValue().length() == 0) {
                    cell.setCellValue(key);
                }
                columnMap.put(key, new PoiColumnData(anColumn, field, cell));
                columnCells.add(cell);
            }
        }

        // 设置列数据
        Collections.sort(columnCells);
        columnRow.setCells(columnCells);
        int columnSize = columnCells.size();
        // 重新设置列号
        for (int i = 0; i < columnSize; i++) {
            columnCells.get(i).setColumnIndex(i);
        }

        // 获取数据行数
        ArrayList<PoiRow> poiRows = createPoiRows(dataList, columnMap);
        Collections.sort(poiRows);

        PoiTable poiTable = new PoiTable();
        // 判断是否存在标题
        String title = anTable.title();
        createPoiTitle(title, columnSize, poiTable);

        poiTable.setSheetName(sheetName);
        poiTable.setColumnRow(columnRow);
        poiTable.setRows(poiRows);
        return poiTable;
    }

    /**
     * 生成poiRow集合
     *
     * @param dataList  数据集合
     * @param columnMap 列名
     * @return
     * @author LiCongLu
     * @date 2020-01-16 15:08
     */
    private static <T> ArrayList<PoiRow> createPoiRows(ArrayList<T> dataList, HashMap<String, PoiColumnData> columnMap) {
        ArrayList<PoiRow> poiRows = new ArrayList<>();

        int rowNum = 1;

        // 遍历获取行数据集合
        for (T dataItem : dataList) {
            PoiRow poiRow = new PoiRow();
            ArrayList<PoiCell> poiCells = new ArrayList<>();
            poiRow.setRowNum(rowNum++).setCells(poiCells);

            // 获取行数据
            for (Map.Entry<String, PoiColumnData> entry : columnMap.entrySet()) {
                PoiColumnData columnData = entry.getValue();
                AnColumn anColumn = columnData.anColumn;
                String value = "";

                PoiCell poiCell = new PoiCell();
                poiCell.setRowNum(poiRow.getRowNum()).setColumnIndex(columnData.columnCell.getColumnIndex());

                try {
                    value = StringUtil.getObjectValue(columnData.field.get(dataItem), anColumn.pattern());
                } catch (Exception e) {
                    value = "";
                }
                poiCell.setCellValue(anColumn.prefix() + value + anColumn.suffix());
                poiCells.add(poiCell);
            }
            Collections.sort(poiCells);
            poiRow.setCells(poiCells);
            poiRows.add(poiRow);
        }
        return poiRows;
    }

    /**
     * 遍历临时类
     *
     * @author LiCongLu
     * @date 2020-01-02 15:53
     */
    private static class PoiColumnData {
        public PoiColumnData(AnColumn anColumn, Field field, PoiCell columnCell) {
            this.anColumn = anColumn;
            this.field = field;
            this.columnCell = columnCell;
        }

        public AnColumn anColumn;
        public Field field;
        public PoiCell columnCell;
    }

    /**
     * 创建表标题
     *
     * @param title      标题
     * @param columnSize 列数
     * @param poiTable   结构表
     * @return
     * @author LiCongLu
     * @date 2020-03-07 19:14
     */
    public static void createPoiTitle(String title, int columnSize, PoiTable poiTable) {
        if (title != null && title.length() > 0) {
            PoiTitle poiTitle = new PoiTitle();
            poiTitle.setRowNum(0).setColumnIndex(0).setCellValue(title);
            if (columnSize > 1) {
                poiTitle.setCellRangeAddress(0, 0, poiTitle.getColumnIndex(), columnSize - 1);
            }
            poiTable.setTitleCell(poiTitle).setColumnRowNum(1);
        }
    }

    /**
     * 生成Poi数据表
     *
     * @param sheetName   表名
     * @param title       表标题
     * @param columnNames 列名
     * @param dataList    数据集合
     * @return
     * @author LiCongLu
     * @date 2020-03-07 18:36
     */
    public static PoiTable createPoiTable(String sheetName, String title, ArrayList<String> columnNames, ArrayList<ArrayList<String>> dataList) {
        // 当无标题时返回空
        if (columnNames == null || columnNames.size() == 0) {
            return null;
        }

        // 当无数据时返回空
        if (dataList == null) {
            return null;
        }

        // 设置默认表名
        if (sheetName == null || sheetName.length() == 0) {
            sheetName = "Sheet1";
        }

        // 解析反射获取列数据
        PoiRow columnRow = new PoiRow();
        ArrayList<PoiCell> columnCells = new ArrayList<>();
        int columnSize = columnNames.size();
        // 遍历生成列名
        for (int i = 0; i < columnSize; i++) {
            String columnName = columnNames.get(i);
            PoiCell cell = new PoiCell();
            cell.setColumnIndex(i)
                    .setCellValue(columnName);
            if (cell.getCellValue().length() == 0) {
                cell.setCellValue("column" + i);
            }
            columnCells.add(cell);
        }

        // 设置列数据
        Collections.sort(columnCells);
        columnRow.setCells(columnCells);

        // 获取数据行数
        ArrayList<PoiRow> poiRows = createPoiRows(dataList, columnSize);
        Collections.sort(poiRows);

        PoiTable poiTable = new PoiTable();

        // 判断是否存在标题
        createPoiTitle(title, columnSize, poiTable);

        poiTable.setSheetName(sheetName);
        poiTable.setColumnRow(columnRow);
        poiTable.setRows(poiRows);
        return poiTable;
    }

    /**
     * 生成poiRow集合
     *
     * @param dataList   数据集合
     * @param columnSize 列长度
     * @return
     * @author LiCongLu
     * @date 2020-03-07 18:48
     */
    private static ArrayList<PoiRow> createPoiRows(ArrayList<ArrayList<String>> dataList, int columnSize) {
        ArrayList<PoiRow> poiRows = new ArrayList<>();
        int rowNum = 1;
        // 遍历获取行数据集合
        for (ArrayList<String> arrayList : dataList) {
            PoiRow poiRow = new PoiRow();
            ArrayList<PoiCell> poiCells = new ArrayList<>();
            poiRow.setRowNum(rowNum++).setCells(poiCells);
            // 获取行数据
            int arraySize = arrayList.size();
            for (int i = 0; i < arraySize && i < columnSize; i++) {
                PoiCell poiCell = new PoiCell();
                poiCell.setRowNum(poiRow.getRowNum())
                        .setColumnIndex(i);
                poiCell.setCellValue(arrayList.get(i));
                poiCells.add(poiCell);
            }
            Collections.sort(poiCells);
            poiRow.setCells(poiCells);
            poiRows.add(poiRow);
        }
        return poiRows;
    }

    /**
     * 读物Excel表格并返回数据集合
     *
     * @param filePath 文件路径
     * @param clazz    返回集合类型
     * @return
     * @author LiCongLu
     * @date 2020-01-16 15:11
     */
    public static <T> ArrayList<T> readXLSXFile(String filePath, Class<T> clazz) {
        return readXLSXFile(filePath, clazz, 0, null);
    }

    /**
     * 读物Excel表格并返回数据集合
     *
     * @param filePath 文件路径
     * @param clazz    返回集合类型
     * @param index    表格索引
     * @return
     * @author LiCongLu
     * @date 2020-01-16 15:11
     */
    public static <T> ArrayList<T> readXLSXFile(String filePath, Class<T> clazz, int index) {
        return readXLSXFile(filePath, clazz, index, null);
    }

    /**
     * 读物Excel表格并返回数据集合
     *
     * @param filePath  文件路径
     * @param clazz     返回集合类型
     * @param sheetName 表格名称
     * @return
     * @author LiCongLu
     * @date 2020-01-16 15:11
     */
    public static <T> ArrayList<T> readXLSXFile(String filePath, Class<T> clazz, String sheetName) {
        return readXLSXFile(filePath, clazz, 0, sheetName);
    }

    /**
     * 读物Excel表格并返回数据集合
     *
     * @param filePath  文件路径
     * @param clazz     返回集合类型
     * @param index     表单索引
     * @param sheetName 表格名称
     * @return
     * @author LiCongLu
     * @date 2020-01-16 15:11
     */
    public static <T> ArrayList<T> readXLSXFile(String filePath, Class<T> clazz, int index, String sheetName) {
        ArrayList<T> dataList = new ArrayList<>();
        if (!clazz.isAnnotationPresent(AnTable.class)) {
            return dataList;
        }

        // 读取表格数据
        ArrayList<HashMap<String, Object>> dataTable = PoiUtil.readXLSXFile(filePath, index, sheetName);

        // 未读取到数据
        if (dataTable == null || dataTable.size() == 0) {
            return dataList;
        }

        HashMap<String, Field> fieldMap = ReflectUtil.getFieldMapByClass(clazz);
        HashMap<String, Field> columnMap = new HashMap<>(16);

        // 获取读取列名
        for (Map.Entry<String, Field> entry : fieldMap.entrySet()) {
            Field field = entry.getValue();
            if (field.isAnnotationPresent(AnColumn.class)) {
                String key = entry.getKey();
                AnColumn anColumn = field.getAnnotation(AnColumn.class);
                if (anColumn != null) {
                    String value = anColumn.value();
                    columnMap.put(value != null && value.length() > 0 ? value.toLowerCase() : key, field);
                }
            }
        }

        // 遍历创建数据集合
        for (HashMap<String, Object> dataRow : dataTable) {
            try {
                T bean = clazz.newInstance();
                for (Map.Entry<String, Field> entry : columnMap.entrySet()) {
                    if (dataRow.containsKey(entry.getKey())) {
                        try {
                            entry.getValue().set(bean, dataRow.get(entry.getKey()));
                        } catch (Exception e) {
                            ReflectUtil.setFieldValue(entry.getValue(), bean, StringUtil.getObjectValue(dataRow.get(entry.getKey())));
                        }
                    }
                }
                dataList.add(bean);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return dataList;
    }
}
